#include <cstdint>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
struct Error {
  Error(std::string desc, std::string file, std::string func, uint64_t line)
      : description(desc) {
    stacktrace.push_back(std::make_tuple(file, func, line));
  }
  std::string description;
  std::vector<std::tuple<std::string, std::string, uint64_t>> stacktrace;
};

std::ostream &operator<<(std::ostream &os, const Error &error) {
  os << "error: " << error.description << std::endl;
  for (auto [file, func, line] : error.stacktrace) {
    os << func << " at (" << file << " : " << line << ')' << std::endl;
  }
  return os;
}
#define THROW(desc)                                                            \
  return tl::unexpected<Error>(                                                \
      Error((desc), __FILE__, __PRETTY_FUNCTION__, __LINE__));

#define TRY(expr)                                                              \
  ({                                                                           \
    auto _ret = (expr);                                                        \
    if (!_ret.has_value()) {                                                   \
      auto _err = _ret.error();                                                \
      _err.stacktrace.push_back(                                               \
          std::make_tuple(__FILE__, __PRETTY_FUNCTION__, __LINE__));           \
      return tl::unexpected<Error>(_err);                                      \
    }                                                                          \
    *_ret;                                                                     \
  })

#define SMART_PTR_TRY_CALL(ptr, mem_fun, ...)                                  \
  ({                                                                           \
    auto _raw_ptr = (ptr).get();                                               \
    if (_raw_ptr == nullptr) {                                                 \
      THROW("null pointer dereference");                                       \
    }                                                                          \
    _raw_ptr->mem_fun(__VA_ARGS__);                                            \
  })
#define CATCH(expr)                                                            \
  {                                                                            \
    auto _ret = (expr);                                                        \
    if (!_ret.has_value())                                                     \
      std::cerr << _ret.error() << std::endl;                                  \
  }
